part of simple_state_driver;

typedef ValueBuilder<T> = Widget Function(BuildContext context, T value, Widget? child);
typedef UpdateBuilder = Widget Function(BuildContext context, Updater updater);
typedef ShouldUpdate<T> = bool Function(T newValue, T oldValue);
typedef ToString<T> = String Function(T value);


typedef InstanceCreator<T> = T Function();

class ManagerInstance {
  final Map<String, dynamic> _datas = {};

  T put<T>(InstanceCreator<T> creator, {String? tag}) {
    final key = _getKey(T, tag);
    if (_datas.containsKey(key)) {
      var data = _datas[key];
      if (data is T) return data;
    }
    T newData = creator();
    _datas[key] = newData;
    return newData;
  }

  T find<T>({String? tag, String? key}) {
    final newKey = key ?? _getKey(T, tag);
    var data = _datas[newKey];
    if (data is T) return data;
    throw '"$T" not found. You need to call "Simple.put($T())""';
  }

  bool delete<T>({String? tag, String? key}) {
    final newKey = key ?? _getKey(T, tag);
    if (!_datas.containsKey(newKey)) {
      if (kDebugMode) {
        print('Instance "$newKey" already removed.');
      }
      return false;
    }
    _datas.remove(newKey);
    if (kDebugMode) {
      print('Instance "$newKey" deleted.');
    }
    return true;
  }

  String _getKey(Type type, String? name) {
    return name == null ? type.toString() : type.toString() + name;
  }
}

mixin SimpleStateController {
  final UpdateManager updateManager = UpdateManager();
  final StoreManager storeManager = StoreManager();

  DataStorer? get dataStorer => storeManager.dataStorer;
  set dataStorer(DataStorer? dataStorer) =>
      storeManager.dataStorer = dataStorer;

  List<Object> initStateKeys(List<Object> stateKeys) {
    storeManager.initStateKeys(stateKeys);
    return stateKeys;
  }

  void updateComponent(String key) => updateManager.updateComponent(key);

  void updateComponents(List<String> keys) =>
      updateManager.updateComponents(keys);

  void updateState(Object key) => updateManager.updateState(key);

  void updateStates(List<Object> keys) => updateManager.updateStates(keys);

  void addUpdater(
      String? componentKey, List<Object> stateKeys, Updater updater) {
    updateManager.addUpdater(componentKey, stateKeys, updater);
  }

  void removeUpdater(
      String? componentKey, List<Object> stateKeys, Updater updater) {
    updateManager.removeUpdater(componentKey, stateKeys, updater);
  }

  void dispose() {
    updateManager.clearUpdaters();
  }
}

class UpdateManager {
  final Map<String, Updater> componentUpdaters = {};
  final Map<Object, List<Updater>> stateUpdaters = {};

  void updateComponent(String key) {
    componentUpdaters[key]?.call();
  }

  void updateComponents(List<String> keys) {
    for (var key in keys) {
      componentUpdaters[key]?.call();
    }
  }

  void updateState(Object key) {
    final updaters = stateUpdaters[key];
    if (updaters != null) {
      for (final updater in updaters) {
        updater();
      }
    } else if (key is StorableState) {
      key.doUpdate();
    }
  }

  void updateStates(List<Object> keys) {
    Set<Updater> set = {};
    for (var key in keys) {
      final updaters = stateUpdaters[key];
      if (updaters != null) {
        set.addAll(updaters);
      } else if (key is StorableState) {
        set.addAll(key.updaters);
      }
    }
    for (final updater in set) {
      updater();
    }
  }

  void addUpdater(
      String? componentKey, List<Object> stateKeys, Updater updater) {
    if (componentKey != null) componentUpdaters[componentKey] = updater;
    for (var key in stateKeys) {
      if (key is StorableState) {
        key.addUpdater(updater);
        break;
      }
      var valueUpdaterList = stateUpdaters[key];
      if (valueUpdaterList != null) {
        valueUpdaterList.add(updater);
      } else {
        stateUpdaters[key] = [updater];
      }
    }
    if (kDebugMode) _printInfo(stateKeys, 'add');
  }

  void removeUpdater(
      String? componentKey, List<Object> stateKeys, Updater updater) {
    if (componentKey != null) componentUpdaters.remove(componentKey);
    for (var key in stateKeys) {
      if (key is StorableState) key.removeUpdater(updater);
      stateUpdaters[key]?.remove(updater);
    }
    if (kDebugMode) _printInfo(stateKeys, 'remove');
  }

  void _printInfo(List<Object> stateKeys, String operator) {
    for (var key in stateKeys) {
      var length = 0;
      if (key is StorableState) {
        length = key.updaters.length;
      } else {
        length = stateUpdaters[key]?.length ?? 0;
      }
      print('\'$key\' $operator updaters, number:$length');
    }
  }

  void clearUpdaters() {
    componentUpdaters.clear();
    stateUpdaters.clear();
  }
}

class StoreManager {
  DataStorer? dataStorer;

  E? get<E>(Object key, {E? defaultValue}) {
    return dataStorer?.get(key, defaultValue: defaultValue);
  }

  Future<void> save<E>(Object key, E value) async {
    return dataStorer?.put(key, value);
  }

  Future<void> delete(Object key) async {
    return dataStorer?.delete(key);
  }

  void setDataStorer(DataStorer? dataStorer) => this.dataStorer = dataStorer;

  void initStateKeys(List<Object> valueKeys) =>
      dataStorer?.initValues(valueKeys);
}